<?php

namespace App\App\Services;

use App\Contracts\Services\Storage\StorageService as StorageServiceContract;
use App\Events\ActivityPaused;
use App\Models\Activity\Activity;
use App\Models\ActivityModel\ActivityModel;
use App\Models\Enroll\Enroll;
use App\Models\Resource\Resource;
use App\Models\VoteCandidate\VoteCandidate;
use App\Morefun\Publishing\Publisher;
use Illuminate\Container\Container;
use Illuminate\Support\Collection;
use Illuminate\Support\Arr;
use Ramsey\Uuid\Uuid;

class VoteCacheService
{
    const ACT_MODEL_VOTE = 'vote';
    protected $redis;
    protected $storage;

    protected $itemNeedFetched = [
        'id',
        'activity_id',
        'type',
        'title',
        'content',
        'cover_url',
        'resource_id',
        'resource_url',
        'from',
        'code',
    ];

    public function __construct(StorageServiceContract $storage)
    {
        $this->redis = Container::getInstance()->make('redis');
        $this->storage = $storage;
    }

    public function cached($activityId)
    {
        return $this->redis->exists('act:'.$activityId) && $this->redis->exists('ranking:'.$activityId);
        //return $this->redis->exists('rules:'.$activityId) && $this->redis->exists('ranking:'.$activityId);
    }

    public function unCached($activityId)
    {
        return event(new ActivityPaused($activityId));
    }

    public function makeCache($activityId)
    {
        $activity = $this->getMatchedActivity($activityId);

        if (null == $activity) {
            return false;
        }

        return $this->redis->pipeline(function ($pipe) use ($activity) {
            $id = $activity->getAttribute('id');
            $this->makeVoteCaches($pipe, $activity);
            $candidates = $this->getCandidatesByActivity($id);
            $this->makeCandidateCache($pipe,$id, $candidates);
        });
    }

    public function addCandidateCache($candidateId)
    {
        $candidate = $this->getCandidate($candidateId);

        if (null === $candidate) {
            return false;
        }

        if (in_array($candidate->type, [VoteCandidate::TYPE_VIDEO, VoteCandidate::TYPE_AUDIO])) {
            $resourceUrl = $this->getResourceUrl($candidate->resource_id);

            if (!$resourceUrl) {
                return false;
            }

            $candidate->resource_url = $resourceUrl;
        }
        if($candidate->from == 3){
            $content = \GuzzleHttp\json_decode($candidate->content, true);
            if(Arr::exists($content, 'text.姓名')){
                Arr::forget($content, 'text.姓名');
            }
            if(Arr::exists($content, 'text.电话')) {
                Arr::forget($content, 'text.电话');
            }
            $candidate->content = \GuzzleHttp\json_encode($content);
        }
        $act = $candidate->activity_id;
        $id = $candidate->id;

        // $searched = $this->redis->hget('search:'.$act, md5($candidate->title));

        return $this->redis->pipeline(function ($pipe) use ($candidate, $act, $id) {
            $candidate->cover_url = $this->storage->toHttpUrl($candidate->cover_url);
            $act = $candidate->activity_id;

            $rankingKey = 'ranking:'.$act;
            $scoreKey = 'scores:'.$act;
            $itemListKey = 'items:'.$act;
            $itemIdsKey = 'ids:'.$act;

            $pipe->zadd($rankingKey, 0, $id);
            $pipe->hset($scoreKey, $id, 0);
            $pipe->lpush($itemListKey, json_encode($candidate));
            $pipe->lpush($itemIdsKey, $id);

            /*
            if ($searched) {
                $pipe->hset('search:'.$act, md5($candidate->title), $searched.'||'.$id);
            } else {
                $pipe->hset('search:'.$act, md5($candidate->title), $id);
            }
            */

            $pipe->set(sprintf('_item:%s:%s', $act, $id), json_encode($candidate));
        });
    }

    public function deleteCandidateCache($candidateId)
    {
        $candidate = $this->getCandidate($candidateId);

        if (null == $candidate) {
            return false;
        }

        $activityId = $candidate->activity_id;
        $id = $candidate->id;
        $this->redis->del(sprintf('_item:%s:%s', $activityId, $id));
        $this->redis->srem(sprintf('ranking:%s', $activityId), $id);
        $this->redis->hdel(sprintf('scores:%s', $activityId), $id);
        $this->redis->lrem(sprintf('ids:%s', $activityId), $id);

        /*
        $searchKey = sprintf('search:%s', $activityId);
        $searchKw = md5($candidate->title);
        $searched = $this->redis->hget($searchKey, $searchKw);
        $parsed = explode('||', $searched);
        $hit = count($parsed);

        if ($hit == 1) {
            $this->redis->hdel($searchKey, $searchKw);
        } elseif ($hit > 1) {
            Arr::forget($parsed, $id);
            $this->redis->hset($searchKey, $searchKw, $parsed);
        }
        */

        $candidate->cover_url = $this->storage->toHttpUrl($candidate->cover_url);

        return $this->redis->lrem(sprintf('items:%s', $activityId), json_encode($candidate));
    }

    public function getNeedToCacheActivities()
    {
        $voteModelId = ActivityModel::query()
            ->where('rule', self::ACT_MODEL_VOTE)
            ->value('id');

        if (!$voteModelId) {
            return [];
        }

        return Activity::query()
            ->where(['model_id' => $voteModelId, 'isPause' => 0])
            ->orderBy('updated_at', 'desc')
            ->get();
    }

    protected function makeVoteCaches($pipe, Activity $activity)
    {
        $applyId = null;
        $rule = \GuzzleHttp\json_decode($activity->rule);

        if (
            isset($rule->relevanceEnrollOn) &&
            $rule->relevanceEnrollOn == 'on' &&
            isset($rule->relevanceEnroll) &&
            Uuid::isValid($rule->relevanceEnroll)
        ) {
            $applyId = $rule->relevanceEnroll;
        }

        $shares = \GuzzleHttp\json_decode($activity->getAttribute('shares'), true);
        $thumb = Arr::get($shares, 'icon', '');
        if(empty($thumb)){
            $thumb = Arr::get($shares, 'thumb', '');
        }
        $thumb = $this->storage->toHttpUrl($thumb);
        $summary = json_encode([
            'id' => $activity->id,
            'name' => $activity->name,
            'description' => $activity->description,
            'start_time' => date('Y.m.d H:i', strtotime($activity->startTime)),
            'end_time' => date('Y.m.d H:i', strtotime($activity->endTime)),
            'background_image' => $this->storage->toHttpUrl($activity->backgroundImg),
            'linked_act_id' => $applyId,
            'app_url' => $activity->getAttribute('app_url'),
            'publishing' => (new Publisher)->formatJsonString($activity->publishing_ways),
            'shares' => [
                'isopen' => Arr::get($shares, 'isopen', 'off'),
                'title' => Arr::get($shares, 'title', ''),
                'content' => Arr::get($shares, 'content', ''),
                'thumb' => $thumb,
                'icon' => $thumb,
            ],
        ]);

        $limit = \GuzzleHttp\json_decode($activity->limit);

        $pipe->set(sprintf('act:%s', $activity->id), $summary);

        $pipe->hmset(
            sprintf('rules:%s', $activity->id),
            'limit',
            $limit->num,
            'type',
            $limit->type,
            'start_at',
            strtotime($activity->startTime),
            'end_at',
            strtotime($activity->endTime),
            'pause',
            $activity->isPause
        );
    }

    protected function makeCandidateCache($pipe, $id, Collection $candidates)
    {
        $rankingKey = 'ranking:'.$id;
        $scoreKey = 'scores:'.$id;
        // $searchKey = 'search:'.$id;
        $itemListKey = 'items:'.$id;
        $itemIdsKey = 'ids:'.$id;
        // $searchMaps = [];

        $ranks = $this->redis->zrevrangebyscore($rankingKey, '+inf', 0, 'WITHSCORES');
        $scores = $this->redis->hgetall($scoreKey);
        $this->redis->del($itemIdsKey, $itemListKey);

        foreach ($candidates as $of => $candidate) {
            if($candidate->from == Enroll::FROM_ENROLLS_JSON){
                $content = \GuzzleHttp\json_decode($candidate->content, true);
                if( Arr::exists($content, 'text')){
                    Arr::forget($content['text'], '姓名');
                    Arr::forget($content['text'], '电话');
                }
                $candidate->content = \GuzzleHttp\json_encode($content);
            }
            $candidate->cover_url = $this->storage->toHttpUrl($candidate->cover_url);

            // 如果是音频和视频，就去读取resource表中的地址
            if (in_array($candidate->type, [VoteCandidate::TYPE_VIDEO, VoteCandidate::TYPE_AUDIO])) {
                $resourceUrl = $this->getResourceUrl($candidate->resource_id);
                if (!$resourceUrl) {
                    continue;
                }

                $candidate->resource_url = $resourceUrl;
            } else {
                $candidate->resource_url = $this->storage->toHttpUrl($candidate->resource_url);
            }

            $candidateText = json_encode($candidate);
            $pipe->set(sprintf('_item:%s:%s', $id, $candidate->id), $candidate);
            $pipe->rpush($itemListKey, $candidateText);
            $pipe->rpush($itemIdsKey, $candidate->id);

            //防止暂停后票数被覆盖，故不种这个缓存
            if (!array_key_exists($candidate->id, $ranks)) {
                $pipe->zadd($rankingKey, 0, $candidate->id);
            }

            //防止暂停后票数被覆盖，故不种这个缓存
            if (!array_key_exists($candidate->id, $scores)) {
                $pipe->hset($scoreKey, $candidate->id, 0);
            }

            // $searchMaps[md5($candidate->title)][] = $candidate->id;
        }
        /*
        foreach ($searchMaps as $named => $ids) {
            $pipe->hset($searchKey, $named, implode('||', $ids));
        }
        */
    }

    protected function getMatchedActivity($id)
    {
        return Activity::query()->where('id', $id)->first();
    }

    protected function getCandidatesByActivity($activityId)
    {
        return VoteCandidate::with('resource')
            ->where('activity_id', $activityId)
            ->where('status', VoteCandidate::SHELVES_ON)
            ->orderBy('weight', 'DESC')
            ->orderBy('code', 'ASC')
            ->get($this->itemNeedFetched);
    }

    protected function getCandidate($candidateId)
    {
        return VoteCandidate::query()
            ->where('id', $candidateId)
            ->first($this->itemNeedFetched);
    }

    protected function getResourceUrl($resourceId)
    {
        return Resource::query()
            ->where('resource_id', $resourceId)
            ->orderBy('id', 'DESC')
            ->value('resource_url');
    }
}
